﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace TByte.XnaHelper.GameObjects
{
    /// <summary>
    /// Camera used to transform the spritebatch when drawing
    /// </summary>
    public class Camera2DGameObject : GameObject
    {
        #region Fields
        Matrix transformMatrix;
        Vector2 cameraPosition;
        float zoom;
        float rotation;
        float friction = 1;
        List<GameObject> folowPoints = new List<GameObject>();
        SpriteBatch spriteBatch;
        RenderTarget2D renderTarget;
        #endregion

        #region Methods
        public override void Update()
        {
            //Save prev cam
            Camera2DGameObject prevCam = Manager.CurrentCamera;

            //Set current cam
            Manager.CurrentCamera = this;

            //Make sure relative position isnt used;
            RelativePosition = Vector2.Zero;
            
            base.Update();

            //Update location
            if (folowPoints.Count() > 0)
            {
                Vector2 totalVector = Vector2.Zero;
                foreach (GameObject ob in folowPoints)
                {
                    totalVector += ob.Position;
                }
                cameraPosition += (totalVector / folowPoints.Count() - cameraPosition)/friction;
                cameraPosition = new Vector2((int)cameraPosition.X,(int)cameraPosition.Y);
            }

            //Reset cam
            Manager.CurrentCamera = prevCam;
        }
        public override void Draw(SpriteBatch spriteBatch)
        {
            //Save prev cam
            Camera2DGameObject prevCam = Manager.CurrentCamera;

            //Set current cam
            Manager.CurrentCamera = this;

            //save rendertargets
            RenderTargetBinding[] targets = Manager.GraphicsDevice.GetRenderTargets();

            //Set render target
            Manager.GraphicsDevice.SetRenderTarget(renderTarget);
            Manager.GraphicsDevice.Clear(Color.Transparent);

            //Begin spriteBatch
            this.spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, null, null, null, TransformMatrix);

            //Draw children
            base.Draw(this.spriteBatch);

            //End spriteBatch
            this.spriteBatch.End();

            //Reset render target
            Manager.GraphicsDevice.SetRenderTargets(targets);
            Manager.GraphicsDevice.Clear(Color.Transparent);

            //Draw render
            spriteBatch.Draw((Texture2D)renderTarget, Manager.ScreenRectangle, Color.White);

            //Reset cam
            Manager.CurrentCamera = prevCam;
        }
        /// <summary>
        /// Add a gameObject to the camera follow list. 
        /// The camera will center on the position in the weighted center of those objects.
        /// </summary>
        /// <param name="gameObject"></param>
        public void AddFolowPoint(GameObject gameObject)
        {
            folowPoints.Add(gameObject);
        }
        /// <summary>
        /// Remove gameObject from the follow list
        /// </summary>
        /// <param name="gameObject"></param>
        public void RemoveFolowPoint(GameObject gameObject)
        {
            folowPoints.Remove(gameObject);
        }
        /// <summary>
        /// Empty the follow list.
        /// </summary>
        /// <param name="gameObject"></param>
        public void ClearFolowPoints(GameObject gameObject)
        {
            folowPoints.Clear();
        }
        #endregion

        #region Constructors
        public Camera2DGameObject(string name = "Camera GameObject")
            : base(name)
        {
            zoom = 1f;
            cameraPosition = Vector2.Zero;
            rotation = 0f;

            this.renderTarget = new RenderTarget2D(Manager.GraphicsDevice, Manager.GraphicsDevice.Viewport.Width, Manager.GraphicsDevice.Viewport.Height, true, Manager.GraphicsDevice.DisplayMode.Format, DepthFormat.Depth24);
            spriteBatch = new SpriteBatch(Manager.GraphicsDevice);
        }
        public Camera2DGameObject(List<GameObject> folowPoints, string name = "Camera GameObject")
            : base(name)
        {
            zoom = 1f;
            cameraPosition = Vector2.Zero;
            rotation = 0f;

            spriteBatch = new SpriteBatch(Manager.GraphicsDevice);
            this.folowPoints = folowPoints;
            this.renderTarget = new RenderTarget2D(Manager.GraphicsDevice, Manager.GraphicsDevice.Viewport.Width, Manager.GraphicsDevice.Viewport.Height, true, Manager.GraphicsDevice.DisplayMode.Format, DepthFormat.Depth24);
        }
        #endregion

        #region Properties
        public float Zoom { get { return zoom; } set { zoom = value; if (zoom < 0.1f) zoom = 0.1f; } }
        public float Rotation { get { return rotation; } set { rotation = value; } }
        public float Friction { get { return friction; } set { if(value >= 1) friction = value; } } 
        public Vector2 CameraPosition { get { return cameraPosition; } set { cameraPosition = value; } }
        public Matrix TransformMatrix
        {
            get
            {
                transformMatrix = Matrix.CreateTranslation(new Vector3(-cameraPosition.X, -cameraPosition.Y, 0)) * Matrix.CreateRotationZ(rotation) * Matrix.CreateScale(new Vector3(zoom, zoom, 0)) * Matrix.CreateTranslation(new Vector3(Manager.Screen.X * 0.5f, Manager.Screen.Y * 0.5f, 0));
                return transformMatrix;
            }
        }
        #endregion
    }
}
